package com.tbynet.jwp.service.provider;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.phprpc.util.AssocArray;
import org.phprpc.util.PHPSerializer;

import com.jfinal.kit.Kv;
import com.jfinal.kit.Ret;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.IAtom;
import com.jfinal.plugin.activerecord.Page;
import com.jfinal.plugin.ehcache.CacheKit;
import com.tbynet.jwp.framework.kit.PassKit;
import com.tbynet.jwp.model.Usermeta;
import com.tbynet.jwp.model.Users;
import com.tbynet.jwp.service.UserService;

public class UserServiceProvider implements UserService {
	
	private Users userDao = new Users().dao();
	private Usermeta usermetaDao = new Usermeta().dao();
	
	@Override
	public Ret login(String username, String password, boolean keepLogin, String loginIp, String userAgent) {
		username = username.toLowerCase().trim();
		password = password.trim();
		Users user = userDao.findFirst("select * from wp_users where user_login=? or user_email=? limit 1", username, username);
		if (null == user) {
			return Ret.fail("msg", "无效用户名");
		}

		String salt = PassKit.getSalt(user.getUserPass());
		String hashedPass = PassKit.encrypt(password, salt);
		// 未通过密码验证
		if (false == user.getUserPass().equals(hashedPass)) {
			return Ret.fail("msg", "为用户名" + username + "指定的密码不正确");
		}
		
		// 持续登录，过期时间为 1 年，否则为 120 分钟，单位为秒
		long liveSeconds =  keepLogin ? 1 * 365 * 24 * 60 * 60 : 120 * 60;
		// cookie
		int maxAgeInSeconds = (int)(keepLogin ? liveSeconds : -1);
		// 登录时间
		long login = System.currentTimeMillis();
		// expireAt session 的过期时间点，单位毫秒
		long expireAt = login + (liveSeconds * 1000);
		
		// 会话id
		String sessionId = StrKit.getRandomUUID();
		
		try {
			Usermeta meta = getUsermeta(user.getID(), Usermeta.session_tokens);
			if(null == meta) {
				meta = new Usermeta();
			}
			
			meta.setUserId(user.getID());
			meta.setSessionTokens(sessionId, expireAt, loginIp, userAgent, login);
			
			if(null == meta.getUmetaId()) {
				if (false == meta.save()) {
					return Ret.fail("msg", "保存 session token 到数据库失败，请联系管理员");
				}
			} else {
				if (false == meta.update()) {
					return Ret.fail("msg", "保存 session token 到数据库失败，请联系管理员");
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		CacheKit.put(Users.loginUserCacheName, sessionId, user);

		return Ret.ok(Users.sessionIdName, sessionId).set(Users.loginUserCacheName, user).set("maxAgeInSeconds", maxAgeInSeconds); 
	}
	
	@Override
	public Users getLoginUserBySessionId(String sessionId) {
		if (null == sessionId) {
			return null;
		}
		
		Users user = CacheKit.get(Users.loginUserCacheName, sessionId);
		if(null != user) {
			Usermeta meta = getUsermeta(user.getID(), Usermeta.session_tokens);
			if(null != meta) {
				try {
					Kv kv = meta.getSessionTokens(sessionId);
					if(null == kv) {
						meta.delSessionTokens(sessionId);
						meta.update();
						CacheKit.remove(Users.loginUserCacheName, sessionId);
						return null;
					}
					
					long expiration = kv.getLong("expiration");
					//检查会话是否过期
					if(System.currentTimeMillis() > expiration) {
						meta.delSessionTokens(sessionId);
						meta.update();
						CacheKit.remove(Users.loginUserCacheName, sessionId);
						return null;
					}
				} catch(Exception e) {
					e.printStackTrace();
				}
			}
		}
		
		return user;
	}

	@Override
	public void logout(String sessionId) {
		if (null != sessionId) {
			Users user = CacheKit.get(Users.loginUserCacheName, sessionId);
			if(null != user) {
				Usermeta meta = getUsermeta(user.getID(), Usermeta.session_tokens);
				if(null != meta) {
					try {
						meta.delSessionTokens(sessionId);
						meta.update();
						
						CacheKit.remove(Users.loginUserCacheName, sessionId);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
			CacheKit.remove(Users.loginUserCacheName, sessionId);
		}
	}
	
	@Override
	public boolean destroy(String sessionId) {
		if (null != sessionId) {
			Users user = CacheKit.get(Users.loginUserCacheName, sessionId);
			if(null != user) {
				Usermeta meta = getUsermeta(user.getID(), Usermeta.session_tokens);
				if(null != meta) {
					try {
						AssocArray array = meta.getSessionTokens();
						if(null != array && false == array.isEmpty()) {
							//删除map必须用iterator遍历，否则会抛出异常
							Iterator<Map.Entry<String, Object>> it = array.toLinkedHashMap().entrySet().iterator();
							while(it.hasNext()) {
								Map.Entry<String, Object> item = it.next();
								//查找用户其它会话
								if(false == sessionId.equals(item.getKey())) {
									//删除用户其它用户缓存
									CacheKit.remove(Users.loginUserCacheName, item.getKey());
									//删除用户其它会话
									it.remove(); //array.remove(item.getKey());
								}
							}
						}
								
						//php 序列化
						PHPSerializer p = new PHPSerializer();
						byte[] sessions = p.serialize(array);
						
						meta.setMetaValue(new String(sessions));
						return meta.update();
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}
		
		return false;
	}

	@Override
	public Users getUser(Object ID) {
		return userDao.findById(ID);
	}

	@Override
	public List<Usermeta> getUsermetas(Object ID) {
		return usermetaDao.find("select * from wp_usermeta where user_id=?", ID);
	}
	
	@Override
	public List<Usermeta> getUsermetas(Object ID, String... metaKeys) {
		List<Object> paras = new ArrayList<Object>();
		StringBuilder sql = new StringBuilder("select * from wp_usermeta where user_id=?");
		
		paras.add(ID);
		
		for(String key : metaKeys) {
			sql.append(" and meta_key=? ");
			paras.add(key);
		}
		
		return usermetaDao.find(sql.toString(), paras.toArray());
	}
	
	@Override
	public Usermeta getUsermeta(Object ID, String metaKey) {
		return usermetaDao.findFirst("select * from wp_usermeta where user_id=? and meta_key=? limit 1", ID, metaKey);
	}

	@Override
	public boolean update(Users user, List<Usermeta> metas) {
		return Db.tx(new IAtom() {
			
			@Override
			public boolean run() throws SQLException {
				boolean flag = user.update();
				if(false == flag) {
					return false;
				}
				
				for(Usermeta meta : metas) {
					if(null == meta.getUmetaId()) {
						flag = meta.save();
					} else {
						flag = meta.update();
					}
					
					if(false == flag) {
						break;
					}
				}
				return flag;
			}
			
		});
	}
	
	@Override
	public boolean save(Users user) {
		return save(user, user.getMetas());
	}
	
	@Override
	public boolean save(Users user, List<Usermeta> metas) {
		return Db.tx(new IAtom() {
			
			@Override
			public boolean run() throws SQLException {
				boolean flag = user.save();
				if(false == flag) {
					return false;
				}
				
				for(Usermeta meta : metas) {
					meta.setUserId(user.getID());
					flag = meta.save();
					
					if(false == flag) {
						break;
					}
				}
				return flag;
			}
			
		});
	}

	@Override
	public Page<Users> search(int pageNumber, int pageSize, String q) {
		List<Object> paras = new ArrayList<Object>();
		StringBuilder sql = new StringBuilder("from wp_users where 1=1");
		
		if(StrKit.notBlank(q)) {
			sql.append(" and user_login like ? ");
			paras.add("%" + q + "%");
			sql.append(" or user_email like ? ");
			paras.add("%" + q + "%");
			sql.append(" or display_name like ? ");
			paras.add("%" + q + "%");
		}
		
		return userDao.paginate(pageNumber, pageSize, "select *", sql.toString(), paras.toArray());
	}

}
